package com.openlogic.common.logic.decision.tree.engine;

import java.lang.reflect.Array;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.noear.nami.Nami;
import org.noear.solon.Solon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson2.JSON;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.openlogic.api.logic.dto.RuleSqlModelDto;
import com.openlogic.api.logic.service.RemoteRuleModelService;
import com.openlogic.common.core.utils.DateUtils;
import com.openlogic.common.logic.cache.DefaultSqlCache;
import com.openlogic.common.logic.constants.RuleConstants;
import com.openlogic.common.logic.constants.RuleTypeConstants;
import com.openlogic.common.logic.decision.common.DecisionEngine;
import com.openlogic.common.logic.decision.tree.model.TreeNode;
import com.openlogic.common.logic.run.ExecRuleHandler;
import com.openlogic.common.logic.utils.StackExceptionUtils;
import com.openlogic.common.plugins.db.api.utils.DataSourceUtils;
import com.openlogic.common.plugins.spi.enums.DbType;
import com.openlogic.datasource.api.param.ConnectionParam;

import jodd.db.connection.ConnectionProvider;
import net.sf.json.JSONObject;

/**
 * @ClassName: XDecisionTreeEngine
 * @Description: 决策树执行引擎
 * @author weiyongpeng
 * @date 2024年9月25日 下午2:47:31
 */
public class XDecisionTreeEngine extends DecisionEngine{
	
	private static RemoteRuleModelService remoteRuleModelService = Solon.context().getBean(RemoteRuleModelService.class);

	private static final Logger log = LoggerFactory.getLogger(XDecisionTreeEngine.class);
	
	private static long DEFAULT_EXPIRE = 5 * 60 * 1000;
    
	private static DefaultSqlCache sqlCache = new DefaultSqlCache(100, DEFAULT_EXPIRE);
    
	private static Object[] execLogArray = new Object[2];
	
	private static final String execLogMsg = MessageFormat.format("{0} 正常信息 决策树:【开始执行】", DateUtils.formatDate(DateUtils.getNowDate(), DateUtils.YYYY_MM_DD_HH_MM_SS));

	private <T> T putCacheValue(String key, T value, long ttl) {
        sqlCache.put(key, value, ttl);
        return value;
    }

	static {
		initExecLog(execLogArray,execLogMsg,RuleTypeConstants.decisionTree.getCode());
		if (remoteRuleModelService == null) {
			remoteRuleModelService = Nami.builder().create(RemoteRuleModelService.class);
			Solon.context().beanMake(RemoteRuleModelService.class);
		}
	}
	
	public static Object[] getExecLogArray() {
		return execLogArray;
	}

	/**
	* 描述：执行决策树，寻找最优解
	* @Title: doDecision
	* @param root 决策树节点
	* @param param 入参
	* @param id 决策执行id
	* @return
	* @author weiyongpeng
	* @date  2024年9月26日 下午1:41:28
	 */
	public static TreeNode doDecision(TreeNode root, Map<String, Object> param, String id,StringBuilder logs) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		TreeNode treeNode = doDecision(root, param, logs, id, stopWatch);
//		log.warn("id:[{}] 决策路径:[{}]", id, logs);
		return treeNode;
	}


	/**
	* 描述：深度优先遍历DSL算法构建有向无环图DAG
	* @Title: doDecision
	* @param root
	* @param param
	* @param logs
	* @param stopWatch 
	* @return
	* @author weiyongpeng
	* @date  2024年9月26日 上午11:08:10
	 */
	private static TreeNode doDecision(TreeNode root,Map<String, Object> param,
			StringBuilder logs, String id, StopWatch stopWatch) {
		logs.append(root.getText()).append("--->");

		List<TreeNode> next = root.getNextNode();
		
		for (TreeNode treeNode : next) {

			try {
				// 1、判断是否结束递归的条件-是否到达叶子节点
				if (treeNode.getIsLeaf()) {
					logs.append(treeNode.getText());
						//String expressValue = (String) treeNode.getValue();
						String expressValue = doParseExpressValue((String) treeNode.getValue(), param);
						Expression expression = AviatorEvaluator.compile(expressValue, true);
						Object result = expression.execute(param);
						treeNode.setCaclRes(result);
						stopWatch.stop();
						if (result instanceof Date) {
							treeNode.setStartTime(DateUtils.formatDate(new Date(stopWatch.getStartTime())));
							treeNode.setResult(MessageFormat.format("决策执行id:【{1}】;决策执行开始时间:【{2}】;决策路径:【{3}】;决策执行结果:【{4}】;决策执行结束时间:【{5}】;决策总耗时:【{6}】毫秒", id, DateUtils.formatDate(new Date(stopWatch.getStartTime()), DateUtils.YYYY_MM_DD_HH_MM_SS), logs, DateUtils.formatDate((Date)result,DateUtils.YYYY_MM_DD_HH_MM_SS),DateUtils.formatDate(new Date(stopWatch.getStopTime()), DateUtils.YYYY_MM_DD_HH_MM_SS), stopWatch.getTime()));
						}else if(result.getClass().isArray()){
							int length = Array.getLength(result);
							Object[] newArray = new Object[length];
							for (int i = 0; i < length; i++) {
								newArray[i] = Array.get(result, i);
							}
							treeNode.setStartTime(DateUtils.formatDate(new Date(stopWatch.getStartTime())));
							treeNode.setResult(MessageFormat.format("决策执行id:【{0}】;决策执行开始时间:【{1}】;决策路径:【{2}】;决策执行结果:【{3}】;决策执行结束时间:【{4}】;决策总耗时:【{5}】毫秒",id, DateUtils.formatDate(new Date(stopWatch.getStartTime()), DateUtils.YYYY_MM_DD_HH_MM_SS), logs, Arrays.toString(newArray),DateUtils.formatDate(new Date(stopWatch.getStopTime()), DateUtils.YYYY_MM_DD_HH_MM_SS), stopWatch.getTime()));
						}else {
							treeNode.setStartTime(DateUtils.formatDate(new Date(stopWatch.getStartTime())));
							treeNode.setResult(MessageFormat.format("决策执行id:【{0}】;决策执行开始时间:【{1}】;决策路径:【{2}】;决策执行结果:【{3}】;决策执行结束时间:【{4}】;决策总耗时:【{5}】毫秒",id, DateUtils.formatDate(new Date(stopWatch.getStartTime()), DateUtils.YYYY_MM_DD_HH_MM_SS), logs, result, DateUtils.formatDate(new Date(stopWatch.getStopTime()), DateUtils.YYYY_MM_DD_HH_MM_SS), stopWatch.getTime()));
						}
					treeNode.setCaclRes(result);
					treeNode.setExecStatus(RuleConstants.NODE_EXEC_SUCCESS);
					return treeNode;
				}
				
				// 2、如果是非根且是参数,直接跳过
				if(!treeNode.getIsLeaf() && !treeNode.getIsRoot() && treeNode.getNodeType().equals("variableNode")) {
					// 手动设置为根节点继续递归
					treeNode.setIsLeaf(false);
					treeNode.setIsRoot(true);
					return doDecision(treeNode, param, logs, id, stopWatch);
				}
				// 3、获取条件表达式 - 这里需要根据变量的类型，执行不同的AviatorExecutor
				String expressValue = doParseExpressValue((String) treeNode.getValue(), param);
				Expression compile = AviatorEvaluator.compile(expressValue,true);
				boolean result = (boolean) compile.execute(param);
				// 条件成立，执行递归
				if (result) {
					return doDecision(treeNode, param, logs, id, stopWatch);
				}
			} catch (Exception e) {
				log.error("决策树执行异常:[{}]", e);
				treeNode.setStartTime(DateUtils.formatDate(new Date(stopWatch.getStartTime())));
				treeNode.setExecStatus(RuleConstants.NODE_EXEC_ERROR);
				treeNode.setResult(MessageFormat.format("决策执行id:【{0}】;决策树执行异常,异常信息:【{1}】",id, StackExceptionUtils.getExceptionType(e).toString()));
				treeNode.setNextNode(null);
				return treeNode;
			}
			// 3、否则剪支，即不往后继续执行递归操作，而是寻找下一个分支节点继续循环
		}
		TreeNode treeNode = new TreeNode();
		treeNode.setStartTime(DateUtils.formatDate(new Date(stopWatch.getStartTime())));
		treeNode.setExecStatus(RuleConstants.NODE_EXEC_ERROR);
		treeNode.setResult(MessageFormat.format("未匹配到满足条件的分支 log:【{0}】,请修改决策树或者输入值!", log));
		return treeNode;
//		throw new UnsupportedOperationException(MessageFormat.format("未匹配到满足条件的分支 log:【{0}】,请修改决策树或者输入值!", log));
	}

	/**
	* 描述：解析表达式
	* @Title: doParseExpressValue
	* @param value
	* @param param
	* @return
	* @author weiyongpeng
	* @date  2024年12月27日 下午3:15:28
	 */
	@SuppressWarnings("unchecked")
	private static String doParseExpressValue(String value, Map<String, Object> param) {
		String expressValue = value;
		XDecisionTreeEngine xDecisionTreeEngine = new XDecisionTreeEngine();
		// 处理sql参数 @sqlVar2@|10
		JSONObject inputJson = JSONObject.fromObject(param);
		if (expressValue.contains("@") && ExecRuleHandler.extractStrings(expressValue).stream().filter(s -> s.split("\\.").length >= 2).collect(Collectors.toList()).size() > 0) {
			List<String> sqlVarList = ExecRuleHandler.extractStrings(expressValue).stream().filter(s -> s.split("\\.").length >= 2).collect(Collectors.toList());
			Iterator<String> it = inputJson.keys();
			for (String sqlVar : sqlVarList) {
				String[] sqlSplit = sqlVar.split("\\.");
				// 取出需要对比的字段
				String compareVar = sqlSplit[1];
				List<Object> columnValueList = new ArrayList<>();
				while (it.hasNext()) {
					String key = it.next();
					if (sqlSplit[0].equals(key)) {
						List<Map<String,Object>> listObjectFir = (List<Map<String,Object>>) com.alibaba.fastjson.JSONArray.parse((String)sqlCache.get(key));
						if (CollectionUtils.isEmpty(listObjectFir)) {
							listObjectFir = xDecisionTreeEngine.selectRuleSqlParamRes(inputJson, key, param);
						}
						columnValueList = listObjectFir.stream().filter(mapF -> Optional.ofNullable(mapF.get(compareVar)).isPresent()).map(mapV -> {
							return mapV.get(compareVar);
						}).collect(Collectors.toList());
						param.put(sqlVar.replaceAll("\\.", ""), columnValueList);
					}
				}
				expressValue = expressValue.replaceAll("\\@", "").replaceAll("\\.", "");;
			}
		}else { // 不包含sql参数的字段
			List<String> sqlVarList = ExecRuleHandler.extractStrings(expressValue).stream().collect(Collectors.toList());
			for (String key : sqlVarList) {
				List<Map<String,Object>> listObjectFir = Optional.ofNullable(sqlCache.get(key)).isPresent() ? 
						(List<Map<String,Object>>) com.alibaba.fastjson.JSONArray.parse((String)sqlCache.get(key)) : 
						xDecisionTreeEngine.selectRuleSqlParamRes(inputJson, key, param);
				param.put(key, listObjectFir);
			}
			expressValue = expressValue.replaceAll("\\@", "");
		}
		return expressValue;
	}
	
	/**
	* 描述：查询sql参数
	* @Title: selectRuleSqlParamRes
	* @param inputjson
	* @param key
	* @param env
	* @return
	* @author weiyongpeng
	* @date  2024年12月27日 下午3:15:03
	 */
	@SuppressWarnings("unchecked")
	private List<Map<String,Object>> selectRuleSqlParamRes(JSONObject inputjson, String key, Map<String, Object> env) {
		String sqlexp;
		// 1、查询sql语句，并执行sql select SQLEXP FROM CONFIG WHERE NAME = ${name}; 所以决策入参正常传递
		RuleSqlModelDto sqlInfoMap = remoteRuleModelService.listRuleModelInner(key).getData();
		String dbType = sqlInfoMap.getDbType();
		ConnectionParam connectionParam = JSON.parseObject(sqlInfoMap.getConnectionParams(), ConnectionParam.class);
		sqlexp = sqlInfoMap.getSqlexp();
		Iterator<String> its = inputjson.keys();
		if(sqlexp.contains("${")) { // 如果有条件参数
			while (its.hasNext()) {
				String key2 = its.next(); 
				if(!key2.equals("everyNodeList") && !key2.equals("areaInfo")) {
					String tmp = "\\$\\{" + key2 + "\\}";
					sqlexp = sqlexp.replaceAll(tmp , "'"+inputjson.getString(key2)+"'");
				}
			}
		}
		// 执行sql获取结果json
		ConnectionProvider connectionProvider = DataSourceUtils.getJdbcTemplate(DbType.getDbType(dbType),
				connectionParam);
		PreparedStatement prepareStatement;
		try {
			prepareStatement = connectionProvider.getConnection().prepareStatement(sqlexp);
			// 2、转化结果集为json 调用字符串的函数 决策设置需要标识出sql参数 加一个#标识 并缓存
			String sqlStr = (String) Optional.ofNullable(sqlCache.get(key)).orElse(
					putCacheValue(key, JSON.toJSONString(prepareStatement.executeQuery(sqlexp)), DEFAULT_EXPIRE));
			env.put(key, sqlStr);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return (List<Map<String,Object>>) com.alibaba.fastjson.JSONArray.parse((String)sqlCache.get(key));
	}

}
